package org.istone.study.mybatis.core;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Administrator
 * @date Jan 17, 2012 11:53:05 AM
 */
public class MyBatis {

    private static final Logger logger = LoggerFactory.getLogger(MyBatis.class);
    private static ClassLoader classLoader = Thread.currentThread()
	    .getContextClassLoader();
    private static String path = "mybatis/config/mybatis.xml";

    private static SqlSessionFactory factory;

    static {
	try {
	    logger.debug("Create SqlSessionFactory...");
	    InputStream is = Resources.getResourceAsStream(classLoader, path);
	    factory = new SqlSessionFactoryBuilder().build(is);
	} catch (IOException e) {
	    logger.error("Can not found the mybatis configuration file from ["
		    + path + "] .");
	}
    }

    /**
     * 
     * @return
     */
    public static SqlSessionFactory getSessionFactory() {
	if (factory == null) {
	    throw new NullPointerException("The SqlSessionFactory is null.");
	}
	return factory;
    }

    /**
     * 
     * @return
     */
    public static SqlSession getSqlSession() {
	if (factory == null) {
	    return null;
	}
	return getSqlSessionProxy();
    }

    /**
     * @return
     */
    private static SqlSession getSqlSessionProxy() {
	return (SqlSession) Proxy.newProxyInstance(classLoader,
		new Class<?>[] { SqlSession.class },
		new SqlSessionInvocationHandler(factory.openSession()));
    }

    /**
     * 
     * @author Administrator
     * @date Jan 17, 2012 1:29:54 PM
     */
    private static final class SqlSessionInvocationHandler implements
	    InvocationHandler {
	private SqlSession session;
	private boolean isClosed;

	/**
	 * @param session
	 */
	public SqlSessionInvocationHandler(SqlSession session) {
	    this.session = session;
	    this.isClosed = false;
	}

	/**
	 * @param proxy
	 * @param method
	 * @param args
	 * @return
	 * @throws Throwable
	 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
	 *      java.lang.reflect.Method, java.lang.Object[])
	 */
	public Object invoke(Object proxy, Method method, Object[] args)
		throws Throwable {
	    Object result = null;
	    try {
		logger.debug("SqlSession invoking...{" + args.toString() + "}");
		if (!"close".equals(method.getName())) {
		    result = method.invoke(session, args);
		}
	    } catch (Exception e) {
		logger.error("Invoke method [" + method.getName() + "] error.");
		throw new Exception(e);
	    } finally {
		if (!isClosed) {
		    session.close();
		    isClosed = true;
		}
	    }
	    return result;
	}
    }
}
